package core

import (
	"github.com/garyburd/redigo/redis"
	"global/libs/structs"
	"fmt"
	"global/constants"
	"time"
	"github.com/shopspring/decimal"
	"global/libs/ws"
	"github.com/ngaut/log"
)

// 从排序好的交易对列表中获取交易对数据
func GetAnalysisPairSorted(conn redis.Conn, exchangeName, sortBy,
	direction string, limit int) ([]string, error) {
	key := fmt.Sprintf(constants.AnalysisPairSortKey, exchangeName, sortBy, direction)
	return redis.Strings(conn.Do("LRANGE", key, 0, limit - 1))
}

// 保存分析后的交易所所有交易对排序
func SaveAnalysisPairSorted(conn redis.Conn, exchangeName, sortBy, direction string, pairs []string) error {
	key := fmt.Sprintf(constants.AnalysisPairSortKey, exchangeName, sortBy, direction)
	conn.Send("MULTI")
	conn.Send("DEL", key)
	for _, v := range pairs {
		conn.Send("RPUSH", key, v)
	}
	_, err := conn.Do("EXEC")
	return err
}

// 批量获取分析后的交易所24小时总交易量结果
func GetAnalysisExchangeAmount24hDataMulti(conn redis.Conn,
	exchanges []string) ([]*structs.ExchangeTicker, error) {
		conn.Send("MULTI")
		for _, v := range exchanges {
			key := fmt.Sprintf(constants.AnalysisExchangeVolumeKey, v)
			conn.Send("HGETALL", key)
		}
		vals, err := redis.Values(conn.Do("EXEC"))
		if err != nil {
			return nil, err
		}

		tickers := make([]*structs.ExchangeTicker, 0)
		for _, val := range vals {
			var ticker structs.ExchangeTicker
			v, _ := redis.Values(val, nil)
			err = redis.ScanStruct(v, &ticker)
			if err != nil {
				return nil, err
			}
			tickers = append(tickers, &ticker)
		}
		return tickers, nil
}

func GetAnalysisExchangeMetaData(conn redis.Conn, exchangeName string) (*structs.ExchangeBase, error) {
	key := fmt.Sprintf(constants.AnalysisExchangeBaseKey, exchangeName)
	vals, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		return nil, err
	}
	var meta structs.ExchangeBase
	if err = redis.ScanStruct(vals, &meta); err != nil {
		return nil, err
	}
	return &meta, nil
}

func GetAnalysisedExchangeMetaDataMulti(conn redis.Conn,
	exchanges []string) ([]*structs.ExchangeBase, error) {
	conn.Send("MULTI")
	for _, v := range exchanges {
		key := fmt.Sprintf(constants.AnalysisExchangeBaseKey, v)
		conn.Send("HGETALL", key)
	}
	vals, err := redis.Values(conn.Do("EXEC"))
	if err != nil {
		return nil, err
	}

	metas :=  make([]*structs.ExchangeBase, 0)
	for _, val := range vals {
		var metaData structs.ExchangeBase
		v, _ := redis.Values(val, nil)
		err = redis.ScanStruct(v, &metaData)
		if err != nil {
			return nil, err
		}
		metas = append(metas, &metaData)
	}
	return metas, nil
}

// 获取排序后的交易所信息
func GetExchangeWithSort(conn redis.Conn, sortType string, limit int) ([]string, error) {
	key := fmt.Sprintf(constants.AnalysisExchangeVolumeSortKey, sortType)
	return redis.Strings(conn.Do("LRANGE", key, 0, limit-1))
}

// 保存交易所24h交易量排序
func SaveExchangeSortForAmount(conn redis.Conn, sortType string, pairs []string) error {
	key := fmt.Sprintf(constants.AnalysisExchangeVolumeSortKey, sortType)
	conn.Send("MULTI")
	conn.Send("DEL", key)
	for _, v := range pairs {
		conn.Send("RPUSH", key, v)
	}
	_, err := conn.Do("EXEC")
	return err
}

// 获取单个交易对的Ticker数据
func GetPairTickerFromExchange(conn redis.Conn,
	exchangeName string, pair string) (*structs.Ticker, error) {
	key := fmt.Sprintf(constants.AnalysisExchangeTickerKey, exchangeName, pair)
	vals, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		return nil, err
	}
	var t structs.Ticker
	err = redis.ScanStruct(vals, &t)
	if err != nil {
		return nil, err
	}
	return &t, err
}

// 批量获取交易对信息
func GetPairTickersFromExchangeMulti(conn redis.Conn,
	exchangeName string, symbols []string) ([]*structs.Ticker, error) {
	conn.Send("MULTI")
	for _, symbol := range symbols {
		key := fmt.Sprintf(constants.AnalysisExchangeTickerKey, exchangeName, symbol)
		conn.Send("HGETALL", key)
	}
	vals, err := redis.Values(conn.Do("EXEC"))
	if err != nil {
		return nil, err
	}
	tickers := make([]*structs.Ticker, 0)
	for _, val := range vals {
		v, _ := redis.Values(val, nil)
		var t structs.Ticker
		if err = redis.ScanStruct(v, &t); err != nil {
			continue
		}
		tickers = append(tickers, &t)
	}
	return tickers, nil
}

// 保存分析后的交易对信息到交易所集下
func SaveCoinPairTickerToAnalysis(conn redis.Conn, exchange string, tickers []*structs.Ticker) error {
	conn.Send("MULTI")
	for _, v := range tickers {
		key := fmt.Sprintf(constants.AnalysisExchangeTickerKey, exchange, v.Symbol)
		conn.Send("HMSET", redis.Args{}.Add(key).AddFlat(v)...)
	}
	_, err := conn.Do("EXEC")
	return err
}

// 保存交易所交易对
func SaveExchangeMarkets(conn redis.Conn, exchangeName string, markets map[string]interface{}) error {
	key := fmt.Sprintf(constants.AnalysisExchangeMarketKey, exchangeName)
	conn.Send("MULTI")
	for m := range markets{
		conn.Send("SADD", key, m)
	}
	_, err := conn.Do("EXEC")
	return err
}

// 保存各交易所币种的USD价格
func SaveCoinUsdPrice(conn redis.Conn, exchangeName string, prices map[string]*structs.CoinUsdPrice) error {
	conn.Send("MULTI")
	for coin, v := range prices {
		key := fmt.Sprintf(constants.AnalysisCoinUsdPriceKey, exchangeName, coin)
		conn.Send("HMSET", redis.Args{}.Add(key).AddFlat(v)...)
	}
	_, err := conn.Do("EXEC")
	return err
}

// 获取某个币在某个交易所的交易对
func GetPairsInExchangeForCoin(conn redis.Conn, exchangeName, coin string) ([]string, error) {
	key := fmt.Sprintf(constants.AnalysisCoinInExchange, coin, exchangeName)
	log.Infof("key: %s", key)
	return redis.Strings(conn.Do("SMEMBERS", key))
}

// 获取币种元数据
func GetCoinMetaAndTicker(conn redis.Conn, coin string) (*structs.CoinTicker, *structs.CoinBase, error) {
	tickerKey := fmt.Sprintf(constants.AnalysisCoinTickerKey, coin)
	metaKey := fmt.Sprintf(constants.AnalysisCoinBaseKey, coin)

	var ticker structs.CoinTicker
	var meta structs.CoinBase

	v, err := redis.Values(conn.Do("HGETALL", tickerKey))
	if err != nil {
		return nil, nil, err
	}

	if err = redis.ScanStruct(v, &ticker); err != nil {
		return nil, nil, err
	}

	v, err = redis.Values(conn.Do("HGETALL", metaKey))
	if err != nil {
		return nil, nil, err
	}

	if err = redis.ScanStruct(v, &meta); err != nil {
		return nil, nil, err
	}

	return &ticker, &meta, nil
}

// 获取所有排序后的币咱
func GetAllSortedCoins(conn redis.Conn, sortType, direction string) ([]string, error) {
	key := fmt.Sprintf(constants.AnalysisCoinSortAscKey, sortType, direction)
	return redis.Strings(conn.Do("LRANGE", key, 0 ,-1))
}

// 获取币种列表
func GetCoinList(conn redis.Conn, req ws.CoinListRequest, direction string) (int, []string, error) {
	key := fmt.Sprintf(constants.AnalysisCoinSortAscKey, req.Sort, direction)
	result := make([]string, 0)
	length, err := redis.Int(conn.Do("LLEN", key))
	if err != nil || length == 0 {
		return length, result, err
	}

	if req.Page <= 0 {
		req.Page = 1
	}

	var start, stop int
	if req.Direction == constants.SortTypeDesc {
		start = length - req.Page * constants.PageSize
		stop = length - 1
	} else {
		start = 0
		stop = req.Page * constants.PageSize - 1
	}

	if start < 0 {
		start = 0
	}
	if stop >= length {
		stop = length - 1
	}

	// log.Infof("key: %s, start: %d, stop: %d", key, start, stop)
	result, err = redis.Strings(conn.Do("LRANGE", key, start, stop))
	if err != nil {
		return 0, nil, err
	}
	return length, result, err
}

func SaveAnalysisCoinOrder(conn redis.Conn, sortBy string, ct []*structs.CoinTicker, direction string) error {
	key := fmt.Sprintf(constants.AnalysisCoinSortAscKey, sortBy, direction)
	conn.Send("MULTI")
	conn.Send("DEL", key)
	for _, v := range ct {
		if direction == constants.SortTypeDesc {
			conn.Send("RPUSH", key, v.Symbol)
		} else {
			conn.Send("LPUSH", key, v.Symbol)
		}
	}
	_, err := conn.Do("EXEC")
	return err
}

// 获取CoinTicker详情数据
func GetCoinTicker(conn redis.Conn, symbol string) (*structs.CoinTicker, error) {
	key := fmt.Sprintf(constants.AnalysisCoinTickerKey, symbol)
	vals, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		return nil, err
	}
	var t structs.CoinTicker
	if err = redis.ScanStruct(vals, &t); err != nil {
		return nil, err
	}
	return &t, nil
}

// 获取交易所Ticker数据
func GetAnalysisExchangeTickerInfo(conn redis.Conn,
	exchangeName string) (*structs.ExchangeTicker, error) {
	key := fmt.Sprintf(constants.AnalysisExchangeVolumeKey, exchangeName)
	vals, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		return nil, err
	}
	var t structs.ExchangeTicker
	err = redis.ScanStruct(vals, &t)
	if err != nil {
		return nil, err
	}
	return &t, nil
}

// 保存交易所总交易量
func SaveExchangeVolume(conn redis.Conn, exchangeName string, ticker structs.ExchangeTicker) error {
	key := fmt.Sprintf(constants.AnalysisExchangeVolumeKey, exchangeName)
	_, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(&ticker)...)
	return err
}

// 获取币种价格
func GetCoinPrice(conn redis.Conn, coinName string) (string, error) {
	// 先从analysis上获取，如果上面没有，从cmc获取
	key := fmt.Sprintf(constants.AnalysisCoinTickerKey, coinName)
	v, err := redis.String(conn.Do("HGET", key, "close"))
	if err != nil || v == "" {
		cmcKey := fmt.Sprintf(constants.CmcCoinQuoteInfoKey, coinName, "USD")
		v, err = redis.String(conn.Do("HGET", cmcKey, "price"))
		if err != nil {
			return "", err
		}
	}
	return v, nil
}

// 批量获取交易所下面所有交易对的Ticker数据
func GetMultiTickersFromExchanges(conn redis.Conn, name string, symbols map[string]structs.Symbol) ([]*structs.Ticker, error) {
	conn.Send("MULTI")
	for _, s := range symbols {
		key := fmt.Sprintf(constants.TickerExchangeKey, name, s.From + "_" + s.To)
		conn.Send("HGETALL", key)
	}

	res, err := redis.Values(conn.Do("EXEC"))
	if err != nil {
		return nil, err
	}

	tickers := make([]*structs.Ticker, 0)
	for _, v := range res {
		v2, _ := redis.Values(v, nil)
		var ticker structs.Ticker
		redis.ScanStruct(v2, &ticker)
		tickers = append(tickers, &ticker)
	}

	return tickers, nil
}

// 获取交易对的Ticker数据
func GetSymbolTickerInExchange(conn redis.Conn, exchangeName string, symbol structs.Symbol) (*structs.Ticker, error) {

	defer func() {
		if r := recover(); r != nil {
			fmt.Println(r)
		}
	}()

	key := fmt.Sprintf(constants.TickerExchangeKey, exchangeName, symbol.From + "_" + symbol.To)
	v, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		return nil, err
	}

	if len(v) == 0 {
		return nil, nil
	}

	var t structs.Ticker
	if err := redis.ScanStruct(v, &t); err != nil {
		return nil, err
	}
	return &t, nil
}

// 保存交易所元数据
func SaveExchangeMetaData(conn redis.Conn, base structs.ExchangeBase) (bool, error) {
	var err error
	key := fmt.Sprintf(constants.AnalysisExchangeBaseKey, base.Slug)
	_, err = conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(&base)...)
	if err != nil {
		return false, err
	}
	return true, nil
}


// 保存币种基础数据
func SaveCoinBaseInfoIfNotExists(conn redis.Conn, base structs.CoinBase) (bool, error) {
	var err error
	key := fmt.Sprintf(constants.AnalysisCoinBaseKey, base.Symbol)
	_, err = conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(&base)...)
	if err != nil {
		return false, err
	}
	return true, nil
}

// 保存分析的币种Ticker数据
func SaveCoinTickerInfo(conn redis.Conn, ticker *structs.CoinTicker) error {
	key := fmt.Sprintf(constants.AnalysisCoinTickerKey, ticker.Symbol)
	_, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(ticker)...)
	return err
}

// 获取24小时前的对应的时间戳以获取价格
func GetTs24hAgo(conn redis.Conn, coin string, ts int64) (decimal.Decimal, int64, error) {
	tsKey := fmt.Sprintf(constants.AnalysisCoinTimeSortKey, coin)
	val, err := redis.Int64s(conn.Do("ZREVRANGEBYSCORE", tsKey, ts, 0, "LIMIT", 0, 1))
	if err != nil {
		return decimal.Zero, 0, err
	}

	if len(val) == 0 {
		val, err = redis.Int64s(conn.Do("ZRANGEBYSCORE", tsKey, 0, time.Now().Unix(), "LIMIT", 0, 1))
		if err != nil {
			return decimal.Zero, 0, nil
		}
	}

	if len(val) == 0 {
		return decimal.Zero, 0, nil
	}

	timestamp := val[0]
	priceMapKey := fmt.Sprintf(constants.AnalysisCoinTimePriceMapKey, coin)

	price, err := redis.String(conn.Do("HGET", priceMapKey, timestamp))
	if err != nil {
		return decimal.Zero, 0, err
	}

	p, _ := decimal.NewFromString(price)
	return p, timestamp, nil
}

// 清理多余的时间戳和价格
func ClearOtherTsAndPrice(conn redis.Conn, coin string, ts int64) error {
	tsKey := fmt.Sprintf(constants.AnalysisCoinTimeSortKey, coin)
	priceMapKey := fmt.Sprintf(constants.AnalysisCoinTimePriceMapKey, coin)

	keys, err := redis.Int64s(conn.Do("ZRANGEBYSCORE", tsKey, 0, ts - 1))
	if err != nil {
		return err
	}

	conn.Send("MULTI")
	conn.Send("ZREM", redis.Args{}.Add(tsKey).AddFlat(&keys)...)
	conn.Send("HDEL", redis.Args{}.Add(priceMapKey).AddFlat(&keys)...)
	_, err = conn.Do("EXEC")
	return err
}

// 保存时间戳和价格的 Map
func SaveTsAndPriceMap(conn redis.Conn, coin string, price string, t time.Time) error {
	priceMapKey := fmt.Sprintf(constants.AnalysisCoinTimePriceMapKey, coin)
	tsKey := fmt.Sprintf(constants.AnalysisCoinTimeSortKey, coin)
	ts := t.Unix()
	conn.Send("MULTI")
	conn.Send("ZADD", tsKey,  ts, ts)
	conn.Send("HSET", priceMapKey, ts, price)
	_, err := conn.Do("EXEC")
	return err
}

// 保存该币种在交易所中都有哪些交易对
func SaveCoinInExchange(conn redis.Conn, exchangeName string, symbols map[string]map[structs.Symbol]interface{}) error {
	conn.Send("MULTI")
	for coin, exchangeSymbols := range symbols {
		key := fmt.Sprintf(constants.AnalysisCoinInExchange, coin, exchangeName)
		for symbol := range exchangeSymbols {
			conn.Send("SADD", key, symbol.From + "_" + symbol.To)
		}
	}
	_, err := conn.Do("EXEC")
	return err
}